package example;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 1. 面向对象中创建对象，调用指定结构，（属性、方法）等功能，可以不使用反射，也可以使用反射。区别？
 * --不使用反射，需要考虑封装性。
 * --使用反射，还可以调用运行时类中的私有结构。
 * 2.以前创建对象并调用方法的方式，与通过反射创建对象并调用方法对比，场景。
 * --从开发者的角度，开发中主要是完成业务开发，对于相关的对象、方法的调用都是确定的。所以使用非反射方式更多。
 * --因为反射体现了动态性，（可以在运行时动态获取对象所属的类，动态调用相关方法），所以在设计框架时，会大量使用反射。
 * ****框架 = 注解 + 反射 + 设计模式
 * 3.单例模式的懒汉式和饿汉式中，私有化类的构造器了，那么通过反射可以创建单例模式的多个对象吗？
 * --是的！
 * 4.是否与面向对象的封装性有冲突？
 * --封装性体现的是是否建议调用内部API的问题。比如，private声明的结构，意味着不建议调用。
 * --反射：体现的是能否调用的问题。因为类的完整结构都加载到了内存中，所以也就有能力进行调用。
 */
public class ExampleTest {
    /**
     * 使用反射之前可以执行的操作
     */
    @Test
    public void test1() {
        // 1.创建Person类的实例
        Person p1 = new Person();
        // 2.调用属性
        p1.name = "Tom";
        System.out.println(p1.name);
        p1.eat();
    }

    /**
     * 使用反射完成上述操作
     */
    @Test
    public void test2() throws InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        // 1.创建Person类的实例
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        System.out.println(person);

        // 2.调用属性
        Field name = clazz.getField("name");
        name.set(person, "Jerry");
        System.out.println(name.get(person));

        // 3. 调用方法
        Method eat = clazz.getMethod("eat");
        eat.invoke(person);
    }

    /**
     * 出了Person类之后，就不能直接调用Person类中私有结构
     * 但是，可以通过反射的方式，调用Person类中私有的结构
     */
    @Test
    public void test3() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 1.调用私有构造器,创建Person实例
        Class<Person> clazz = Person.class;
        Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person person = declaredConstructor.newInstance("Tom");
        System.out.println(person);

        // 2.调用私有属性
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        age.set(person, 11);
        System.out.println(age.get(person));

        // 3. 调用私有方法
        Method sleep = clazz.getDeclaredMethod("sleep");
        sleep.setAccessible(true);
        sleep.invoke(person);

        System.out.println(person);

    }
}
